//
//  WmcMediaUploadAPI.swift
//  YPWatermarkCamera
//
//  Created by 周诗松 on 2022/6/9.
//

import Alamofire
import CommonCrypto
import Foundation
import Moya
import RxSwift
import SwiftyXMLParser
import YPNetwork

struct WmcMediaUploadAPI {
    /// 申请资源上传
    /// - DocumentUrl: http://rap2.taobao.org/organization/repository/editor?id=302804&mod=511881&itf=2268930
    /// - Parameter model: 模型
    /// - Returns: Observable<WmcMediaUploadResourceModel>
    static func applyUploadUrl(model: WmcMediaUploadResourceParameterModel) -> Observable<WmcMediaUploadResourceModel> {
        let params: [String: Any]? = model.toJSON()
        return WmcMediaRequest.requestData(path: "/media/v1/resource/applyUpload", parameters: params) { request in
            request.method = .post
        }.parseModelWithClass(WmcMediaUploadResourceModel.self)
    }

    /// 申请资源上传
    /// - DocumentUrl: http://rap2.taobao.org/organization/repository/editor?id=302804&mod=511881&itf=2268930
    /// - Parameter paramsDict: 参数字典
    /// - Returns: Observable<WmcMediaUploadResourceModel>
    static func applyUploadUrl(paramsDict: [String: Any]) -> Observable<WmcMediaUploadResourceModel> {
        return WmcMediaRequest.requestData(path: "/media/v1/resource/applyUpload", parameters: paramsDict) { request in
            request.method = .post
        }.parseModelWithClass(WmcMediaUploadResourceModel.self)
    }
    
    static func applyUploadUrl(paramsDict: [String: Any], appId: String? = nil, success: @escaping (WmcMediaUploadResourceModel) -> Void, failure: @escaping (YPNetworkError?) -> Void) {
        WmcMediaRequest.requestData(path: "/media/v1/resource/applyUpload", parameters: paramsDict) { request in
            request.method = .post
            if let appId = appId {
                request.extraHeaderFields = ["appId": appId]
            }
        } success: { (response: YPNetworkResponse) in
            if let dict = response.data as? [String: Any] {
                let result = dict.parseModelWithClass(WmcMediaUploadResourceModel.self)
                success(result)
            } else {
                failure(nil)
            }
        } failure: { error in
            failure(error)
        }
    }

    /// 批量申请资源上传
    /// - DocumentUrl: http://rap2.taobao.org/organization/repository/editor?id=302804&mod=511881&itf=2269425
    /// - Parameter models: 模型数组
    /// - Returns: Observable<[WmcMediaUploadResourceModel]>
    static func applyBatchUploadUrl(models: [WmcMediaUploadResourceParameterModel]) -> Observable<[WmcMediaUploadResourceModel]> {
        var paramsList: [[String: Any]] = []
        models.forEach { model in
            if let dict = model.toJSON() {
                paramsList.append(dict)
            }
        }
        let params = ["files": paramsList]
        return WmcMediaRequest.requestData(path: "/media/v1/resource/applyBatchUpload", parameters: params) { request in
            request.method = .post
        }.parseListModelWithClass(WmcMediaUploadResourceModel.self, designatedPath: "urls")
    }

    /// 批量申请资源上传
    /// - DocumentUrl: http://rap2.taobao.org/organization/repository/editor?id=302804&mod=511881&itf=2269425
    /// - Parameter models: 模型数组
    /// - Returns: Observable<[WmcMediaUploadResourceModel]>
    static func applyBatchUploadUrl(paramsDictList: [[String: Any]]) -> Observable<[WmcMediaUploadResourceModel]> {
        let params = ["files": paramsDictList]
        return WmcMediaRequest.requestData(path: "/media/v1/resource/applyBatchUpload", parameters: params) { request in
            request.method = .post
        }.parseListModelWithClass(WmcMediaUploadResourceModel.self, designatedPath: "urls")
    }
    
    static func applyBatchUploadUrl(paramsDictList: [[String: Any]], success: @escaping ([WmcMediaUploadResourceModel]) -> Void, failure: @escaping (YPNetworkError?) -> Void) {
        let params = ["files": paramsDictList]
        WmcMediaRequest.requestData(path: "/media/v1/resource/applyBatchUpload", parameters: params) { request in
            request.method = .post
        } success: { (response: YPNetworkResponse) in
            if let dict = response.data as? [String: Any], let array = dict["urls"] as? [Any] {
                let result = array.parseListModelWithClass(WmcMediaUploadResourceModel.self)
                success(result)
            } else {
                failure(nil)
            }
        } failure: { error in
            failure(error)
        }
    }

    /// 资源复制
    /// - Parameter model: 参数模型
    /// - Returns: Observable<WmcMediaUploadResourceModel>
    static func copyResource(_ model: WmcMediaCopyResourceParameterModel, appId: String, resourceId: String, secretKey: String) -> Observable<WmcMediaUploadResourceModel> {
        let sign = "appId=${\(appId)}&resourceId=${\(resourceId)}${\(secretKey)}".yp.hashStr(.sha256) ?? ""
        model.copSign = sign
        let params: [String: Any]? = model.toJSON()
        return WmcMediaRequest.requestData(path: "/media/v1/resource/applyCopy", parameters: params) { request in
            request.method = .post
        }.parseModelWithClass(WmcMediaUploadResourceModel.self)
    }
    
    /// 申请访问资源
    /// - DocumentUrl: http://rap2.taobao.org/organization/repository/editor?id=302804&mod=511881&itf=2269007
    /// - Parameter urls: 需要访问的资源地址数组，最大500
    /// - Returns: Observable<YPNetworkResponse>
    static func applyResourceAccess(urls: [String]) -> Observable<YPNetworkResponse> {
        let params: [String: Any]? = ["urls": urls]
        return WmcMediaRequest.requestData(path: "/media/v1/resource/access", parameters: params) { request in
            request.method = .post
        }
    }

    /// 上传资源到相册
    /// - Parameter model: 模型
    /// - Returns: Observable<WmcMediaUploadResourceModel>
    static func applyUploadImageUrl(_ model: WmcMediaUploadImageParameterModel) -> Observable<WmcMediaUploadResourceModel> {
        let params: [String: Any]? = model.toJSON()
        return WmcMediaRequest.requestData(path: "/media/v1/album/applyUpload", parameters: params) { request in
            request.method = .post
        }.parseModelWithClass(WmcMediaUploadResourceModel.self)
    }

    /// 上传文件到阿里云
    /// - Parameters:
    ///   - data: 二进制数据
    ///   - uploadUrl: 上传地址
    ///   - header: 上传的header参数
    ///   - progress: 上传进度回调
    ///   - completion: 上传成功回调
    ///   - failure: 上传失败回调
    /// - Returns: Request
    @discardableResult
    static func uploadImage(data: Data, uploadUrl: String, header: [String: String], progress: ((Double) -> Void)? = nil, completion: (() -> Void)? = nil, failure: ((YPNetworkError) -> Void)? = nil) -> Request? {
        let request = AF.upload(data, to: uploadUrl, method: .put, headers: HTTPHeaders(header), interceptor: nil, fileManager: FileManager.default) { r in
            // 取消超时
            r.timeoutInterval = 0
        }
        
        request.uploadProgress(queue: .main) { progressModel in
            let value: Double = .init(progressModel.completedUnitCount) / Double(progressModel.totalUnitCount)
//            progress?(value)
            WmcLogManager.Log("真实上传进度：\(value)")
        }
        
        let kbCount = Double(data.count) / 1024.0
        let elapsedTime = kbCount / Double(1024 * 2) * 1000
        let increaseValue = 50.0 / elapsedTime
        
        var progressValue: Double = 0.0
        let timer = DispatchSource.makeTimerSource()
        timer.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.milliseconds(50))
        timer.setEventHandler(handler: {
            progressValue += increaseValue
            DispatchQueue.main.async {
                progress?(progressValue)
            }
            if progressValue >= 0.9 {
                timer.cancel()
            }
        })
        timer.resume()


        request.response { response in
            switch response.result {
            case .success:
                let json = response.data
                if (json != nil) {
                    if let data = response.data {
                        // !!!: 阿里云返回的数据很奇葩，有时候返回JSON，有时候返回XML，返回JSON的时候可能会成功也可能会失败，返回XML的时候好像都是失败
                        let dict = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers)
                        if let dict = dict as? [String: Any], let code = dict["code"] as? Int, code == 0 {
                            progress?(1.0)
                            completion?()
                            timer.cancel()
                        } else {
                            let xml = XML.parse(data)
                            let msg = xml["Error", "Message"].text ?? "上传失败"
                            let error = YPNetworkError(upload: msg)
                            failure?(error)
                            timer.cancel()
                        }
                    }
                }
            case let .failure(error):
                let customError = YPNetworkError(upload: error.localizedDescription)
                failure?(customError)
                timer.cancel()
            }
        }

        return request
    }

    /// 上传文件到阿里云
    /// - Parameters:
    ///   - fileURL: 文件路径
    ///   - uploadUrl: 上传地址
    ///   - header: 上传的header参数
    ///   - progress: 上传进度回调
    ///   - completion: 上传成功回调
    ///   - failure: 上传失败回调
    /// - Returns: Request

    @discardableResult
    static func uploadImage(fileURL: URL, uploadUrl: String, header: [String: String], progress: ((Double) -> Void)? = nil, completion: (() -> Void)? = nil, failure: ((YPNetworkError) -> Void)? = nil) -> Request? {

        let request = AF.upload(fileURL, to: uploadUrl, method: .put, headers: HTTPHeaders(header), interceptor: nil, fileManager: FileManager.default, requestModifier: { request in
            /// 这行代码不能省略，上传文件如果不设置Content-Type的话默认是application/octet-stream，阿里云很垃圾会拿该字段的值验证签名，导致签名验证不通过
            request.headers["Content-Type"] = ""
            request.timeoutInterval = 0
        }).uploadProgress(queue: .main) { progressModel in
            /// 有时候阿里云的上传未返回进度
            let value: Double = .init(progressModel.completedUnitCount) / Double(progressModel.totalUnitCount)
//            progress?(value)
        }
        
        var fileSize: Double = 1024.0
        let manager = FileManager.default
        var urlString = fileURL.yp.string
        if urlString.contains("file://") {
            urlString = urlString.replacingOccurrences(of: "file://", with: "")
        }
        if manager.fileExists(atPath: urlString) {
            let dict = try? manager.attributesOfItem(atPath: urlString)
            if let dict = dict as? NSDictionary {
                fileSize = Double(dict.fileSize()) / 1024.0
            }
        }
        
        let elapsedTime = fileSize / 1024.0 * 1000
        let increaseValue = 25.0 / elapsedTime
        
        var progressValue: Double = 0.0
        let timer = DispatchSource.makeTimerSource()
        timer.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.milliseconds(25))
        timer.setEventHandler(handler: {
            progressValue += increaseValue
            DispatchQueue.main.async {
                progress?(progressValue)
            }
            if progressValue >= 0.95 {
                timer.cancel()
            }
        })
        timer.resume()

        request.response { response in
            switch response.result {
            case .success:
                let json = response.data
                if (json != nil) {
                    if let data = response.data {
                        // !!!: 阿里云返回的数据很奇葩，有时候返回JSON，有时候返回XML，返回JSON的时候可能会成功也可能会失败，返回XML的时候好像都是失败
                        let dict = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers)
                        if let dict = dict as? [String: Any], let code = dict["code"] as? Int, code == 0 {
                            progress?(1.0)
                            completion?()
                            timer.cancel()
                        } else {
                            let xml = XML.parse(data)
                            let msg = xml["Error", "Message"].text ?? "上传失败"
                            let error = YPNetworkError(upload: msg)
                            failure?(error)
                            timer.cancel()
                        }
                    }
                }
            case let .failure(error):
                let customError = YPNetworkError(upload: error.localizedDescription)
                failure?(customError)
                timer.cancel()
            }
        }

        return request
    }

    /// 上传文件（multipartFormData方式）
    /// - Parameters:
    ///   - data: 二进制数据
    ///   - uploadUrl: 上传地址
    ///   - header: 上传的header参数
    ///   - progress: 上传进度回调
    ///   - completion: 上传成功回调
    ///   - failure: 上传失败回调
    /// - Returns: Request
    @discardableResult
    static func uploadImageWithMultipartFormData(data: Data, uploadUrl: String, header: [String: String], progress: ((Double) -> Void)? = nil, completion: (() -> Void)? = nil, failure: ((YPNetworkError) -> Void)? = nil) -> Request? {
        let httpHeaders = HTTPHeaders(header)

        let request = AF.upload(multipartFormData: { multiPart in
            multiPart.append(data, withName: "", mimeType: "image/png")
        }, to: uploadUrl, method: .put, headers: httpHeaders).uploadProgress(queue: .main) { progressModel in
            let value: Double = .init(progressModel.completedUnitCount) / Double(progressModel.totalUnitCount)
            progress?(value)
        }

        request.response { response in
            switch response.result {
            case .success:
                let json = response.data
                if (json != nil) {
                    if let data = response.data {
                        let dict = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers)
                        if let dict = dict as? [String: Any], let code = dict["code"] as? Int, code == 0 {
                            completion?()
                        } else {
                            let xml = XML.parse(data)
                            let msg = xml["Error", "Message"].text ?? "上传失败"
                            let error = YPNetworkError(upload: msg)
                            failure?(error)
                        }
                    }
                }
            case let .failure(error):
                let customError = YPNetworkError(upload: error.localizedDescription)
                failure?(customError)
            }
        }

        return request
    }
}
